package com.hanxiaozhang.threadbase1ndedition.no9container.no4concurrentcontainer;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 功能描述: <br>
 * 〈CopyOnWriteArrayList写时复制容器〉
 * <p>
 * ##特点：
 * 读不加锁，写加锁
 * 多线程环境下，写时效率低，读时效率高
 * ##场景：
 * 适合写少读多的环境
 * ##简单原理：
 * 向容器添加元素时，先将容器Copy复制出一个新容器，然后将元素添加到新容器中，再将原容器的引用
 * 指向新容器。
 * 并发读的时候并不需要锁定容器，因为原容器没有变化，所以可以读取原容器中的值。
 * ## 注意：
 * COW容器只能保证数据的最终一致性，不能保证数据实时一致性。
 *
 * @Author:hanxinghua
 * @Date: 2021/11/21
 */
public class No3CopyOnWriteArrayList {

    public static void main(String[] args) {

        // 基础使用
        base();

        runAndComputeTime();

    }

    /**
     * 基础使用
     */
    private static void base() {
        CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        System.out.println(list);
        list.add(3);
        System.out.println(list);
        // 只添加不存在元素
        System.out.println(list.addIfAbsent(3));
        System.out.println(list.addIfAbsent(4));
        System.out.println(list);
    }

    private static void runAndComputeTime() {

        List<String> lists = new CopyOnWriteArrayList<>();

        Random r = new Random();
        Thread[] ths = new Thread[100];

        for (int i = 0; i < ths.length; i++) {
            Runnable task = new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 1000; i++) {
                        lists.add("a" + r.nextInt(10000));
                    }
                }

            };
            ths[i] = new Thread(task);
        }
        long startTime = System.currentTimeMillis();
        Arrays.asList(ths).forEach(t -> t.start());
        Arrays.asList(ths).forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
        System.out.println(lists.size());
    }
}

